﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Rotempco.Core.Logic;
using Rotempco.Core.ORM.Objects.Stores;
using Rotempco.Core.ORM.Objects;

namespace Rotempco.Core.ORM.Logic
{
    public class KrustEncryptionLogic
    {
        private const int MinRotLength = 3;
        private const int MaxRotLength = 20;

        public static SerializableDictionary<char, string> GenerateRotDictionary()
        {
            Random random = new Random();
            SerializableDictionary<char, string> rotDictionary = new SerializableDictionary<char, string>();

            List<char> charsToEncryptList = GetRandomCharList(includeSpecielChars: true);
            List<char> seedCharList = GetRandomCharList(includeSpecielChars: false);

            Dictionary<string, bool> usedRotDictionaryValueCharPairs = new Dictionary<string, bool>();

            foreach (char charToEncrypt in charsToEncryptList)
            {
                string rotDictionaryValue = GetRandomRotDictionaryValue(random, seedCharList, usedRotDictionaryValueCharPairs);
                rotDictionary.Add(charToEncrypt, rotDictionaryValue);
            }

            return rotDictionary;
        }

        public static string Encrypt(string sourceText, bool useSalt = false)
        {
            return Encrypt(sourceText, EncryptionKeyStore.Instance.KrustKey.KrustDictionary, useSalt);
        }

        public static string Encrypt(string sourceText, Dictionary<char, string> rotDictionary, bool useSalt = false)
        {
            Random random = new Random();
            if (useSalt)
                sourceText += GetSalt(random, sourceText);

            sourceText = CleanSourceText(sourceText);

            char[] sourceCharList = sourceText.ToCharArray();

            string encryptedString = string.Empty;

            foreach (char c in sourceCharList)
            {
                if (rotDictionary.ContainsKey(c))
                    encryptedString += rotDictionary[c];
                else
                    encryptedString += c;
            }

            return encryptedString;
        }



        private static string GetSalt(Random random, string sourceText)
        {
            Dictionary<char, int> characterList = (from c in sourceText.Replace(" ", "")
                                                   group c by c into g
                                                   orderby g.Count() descending
                                                   select new { Character = g.Key, Count = g.Count() }).Take(6).ToDictionary(c => c.Character, c => c.Count);
            List<char> saltChars = new List<char>();
            saltChars.Add('☺');
            saltChars.Add('☻');
            saltChars.Add('♥');
            saltChars.Add('♦');
            saltChars.Add('♣');
            saltChars.Add('♠');
            
            List<char> saltList = new List<char>();
            int saltCharIndex = 0;
            foreach (char c in characterList.Keys)
            {
                bool add = random.Next(1) == 1;

                int max = (int)Math.Ceiling(characterList[c] + characterList[c] * 0.2);
                int min = (int)Math.Floor(characterList[c] - characterList[c] * 0.2);

                int numberOfChars = random.Next(min, max);

                for (int i = 0; i < numberOfChars; i++)
                    saltList.Add(saltChars[saltCharIndex]);

                saltCharIndex++;
            }

            RandomizerLogic.RandomizeList(saltList);

            for (int i = saltList.Count - 1; i >= 0; i--)
            {
                int wordLength = random.Next(2, 7);
                i -= wordLength;
                if (i >= 0)
                    saltList.Insert(i, ' ');
            }

            return string.Join("", saltList);
        }

        private static string GetRandomRotDictionaryValue(Random random, List<char> seedCharList, Dictionary<string, bool> usedRotDictionaryValueCharPairs)
        {
            string randomRotDictionaryValue = "";

            for (int i = 0; i < 500; i++)
            {
                randomRotDictionaryValue = GetRandomString(random, seedCharList);

                List<string> charPairList = GetCharPairs(randomRotDictionaryValue);

                if (!RotDictionaryValueIsUnique(usedRotDictionaryValueCharPairs, charPairList))
                    continue;

                foreach (string charPair in charPairList)
                {
                    usedRotDictionaryValueCharPairs.Add(charPair, true);
                }

                return randomRotDictionaryValue;
            }

            throw new Exception("No string found");
        }

        private static string GetRandomString(Random random, List<char> seedCharList)
        {
            string randomString = string.Empty;

            for (int i = 0; i < random.Next(MinRotLength, MaxRotLength + 1); i++)
            {
                randomString += seedCharList[random.Next(seedCharList.Count)];
            }

            return randomString;
        }

        private static List<string> GetCharPairs(string generatedString)
        {
            List<string> charPairList = new List<string>();

            for (int i = 0; i < generatedString.Length - 1; i++)
            {
                charPairList.Add(string.Join(" ", generatedString.Skip(i).Take(2)));
            }

            return charPairList;
        }

        private static bool RotDictionaryValueIsUnique(Dictionary<string, bool> usedEncryptionValues, List<string> encryptionValueList)
        {
            Dictionary<string, bool> encryptionValueTest = new Dictionary<string, bool>();
            foreach (string encryptionValue in encryptionValueList)
            {
                if (encryptionValueTest.ContainsKey(encryptionValue))
                    return false;

                encryptionValueTest.Add(encryptionValue, true);
            }

            foreach (string encryptionValue in encryptionValueList)
            {
                if (usedEncryptionValues.ContainsKey(encryptionValue))
                    return false;
            }

            return true;
        }

        private static List<char> GetRandomCharList(bool includeSpecielChars)
        {
            List<char> sourceList = new List<char>();
            for (char c = 'a'; c <= 'z'; c++)
                sourceList.Add(c);
            for (char c = 'A'; c <= 'Z'; c++)
                sourceList.Add(c);
            for (char c = '0'; c <= '9'; c++)
                sourceList.Add(c);

            if (includeSpecielChars)
            {
                //sourceList.Add(' ');
                sourceList.Add('☺');
                sourceList.Add('☻');
                sourceList.Add('♥');
                sourceList.Add('♦');
                sourceList.Add('♣');
                sourceList.Add('♠');
            }

            RandomizerLogic.RandomizeList(sourceList);

            return sourceList;
        }

        private static Dictionary<string, char> GetInverse(Dictionary<char, string> rotDictionary)
        {
            Dictionary<string, char> inverse = new Dictionary<string, char>();
            foreach (char c in rotDictionary.Keys)
            {
                inverse.Add(rotDictionary[c], c);
            }

            return inverse;
        }

        private static string PhoneticReplace(string s)
        {
            string s2 = s.ToLower();
            s2 = s2.Replace("æ", "ae");
            s2 = s2.Replace("ø", "oe");
            s2 = s2.Replace("å", "aa");
            s2 = s2.Replace("u", "v");
            s2 = s2.Replace("ph", "f");
            s2 = s2.Replace("ch", "k");
            s2 = Regex.Replace(s2, "([gkrst])(h)", "$1");
            s2 = s2.Replace("ds", "s");
            s2 = s2.Replace("nd", "n");
            s2 = s2.Replace("c", "k");
            s2 = s2.Replace("d", "t");
            s2 = s2.Replace("w", "v");
            s2 = s2.Replace("z", "s");
            s2 = s2.Replace("x", "s");

            return s2;
        }

        private static string CleanSourceText(string sourceText)
        {
            sourceText = PhoneticReplace(sourceText);
            sourceText = sourceText.Replace(Environment.NewLine, "");
            sourceText = " " + sourceText + " ";

            string regexExpression = @"([!""#$%&'()*+,-\./:;<=>?@[\]^_`{|}~])";

            sourceText = Regex.Replace(sourceText, regexExpression, "");

            sourceText = Regex.Replace(sourceText, @"(\s[0-9]+\s)", "");

            sourceText = new string(sourceText.ToCharArray().Where(c => !char.IsPunctuation(c)).ToArray());

            List<string> sourceTextList = sourceText.Split(" ".ToArray(), StringSplitOptions.RemoveEmptyEntries).Distinct().ToList();
            sourceTextList = RemoveIgnoreWords(sourceTextList);

            RandomizerLogic.RandomizeList(sourceTextList);

            sourceText = string.Join(" ", sourceTextList);

            return sourceText;
        }

        private static List<string> RemoveIgnoreWords(List<string> searchKeywords)
        {
            List<string> stopWords = new List<string>
            {
                "a",
                "all",
                "am",
                "an",
                "and",
                "any",
                "are",
                "as",
                "at",
                "be",
                "but",
                "can",
                "did",
                "do",
                "does",
                "for",
                "from",
                "had",
                "has",
                "have",
                "here",
                "how",
                "i",
                "if",
                "in",
                "is",
                "it",
                "no",
                "not",
                "of",
                "on",
                "or",
                "so",
                "that",
                "the",
                "then",
                "there",
                "this",
                "to",
                "too",
                "up",
                "use",
                "what",
                "when",
                "where",
                "who",
                "why",
                "you",
                "og",
                "i",
                "jeg",
                "det",
                "at",
                "en",
                "den",
                "til",
                "er",
                "som",
                "på",
                "de",
                "med",
                "han",
                "af",
                "for",
                "ikke",
                "der",
                "var",
                "mig",
                "sig",
                "men",
                "et",
                "har",
                "om",
                "vi",
                "min",
                "havde",
                "ham",
                "hun",
                "nu",
                "over",
                "da",
                "fra",
                "du",
                "ud",
                "sin",
                "dem",
                "os",
                "op",
                "man",
                "hans",
                "hvor",
                "eller",
                "hvad",
                "skal",
                "selv",
                "her",
                "alle",
                "vil",
                "blev",
                "kunne",
                "ind",
                "når",
                "være",
                "dog",
                "noget",
                "ville",
                "jo",
                "deres",
                "efter",
                "ned",
                "skulle",
                "denne",
                "end",
                "dette",
                "mit",
                "også",
                "under",
                "have",
                "dig",
                "anden",
                "hende",
                "mine",
                "alt",
                "meget",
                "sit",
                "sine",
                "vor",
                "mod",
                "disse",
                "hvis",
                "din",
                "nogle",
                "hos",
                "blive",
                "mange",
                "ad",
                "bliver",
                "hendes",
                "været",
                "thi",
                "jer",
                "sådan",                
            };

            return searchKeywords.Where(s => !stopWords.Contains(s.ToLower())).ToList();
        }


        //public static string Decrypt(string encryptedText, Dictionary<char, string> rotDictionary)
        //{
        //    Dictionary<string, char> inverseRotDictionary = GetInverse(rotDictionary);
        //    return Decrypt(encryptedText, inverseRotDictionary);
        //}

        //public static string Decrypt(string encryptedText, Dictionary<string, char> inverseRotDictionary)
        //{
        //    List<char> encryptedCharList = encryptedText.ToCharArray().ToList();
        //    Dictionary<char, bool> charsToEncryptDictionary = GetRandomCharList(includeSpecielChars: true).ToDictionary(c => c, c => true);

        //    string decryptedString = string.Empty;

        //    while (encryptedCharList.Count() > 0)
        //    {
        //        if (!charsToEncryptDictionary.ContainsKey(encryptedCharList[0]))
        //        {
        //            decryptedString += encryptedCharList[0];
        //            encryptedCharList.RemoveAt(0);
        //            continue;
        //        }

        //        for (int i = MinRotLength; i <= MaxRotLength; i++)
        //        {
        //            string testString = new string(encryptedCharList.Take(i).ToArray());
        //            if (inverseRotDictionary.ContainsKey(testString))
        //            {
        //                decryptedString += inverseRotDictionary[testString];
        //                encryptedCharList.RemoveRange(0, i);
        //                break;
        //            }
        //        }
        //    }

        //    return decryptedString;
        //}
    }
}
